<?php
/*
 * Created on 30.12.2005
 * Parser Engine for ppage LALR(1) parser generator tables
 */

/**
 * Parser stack machine for evaluation the parser table and creating AST
 */
class ParserMachine
{

    var $table;
    var $stack;
    
    function ParserMachine ($lexer, $table, $productions, $verbose = false)
    {
        if (!count($table) || !count($productions))
            die ("PARSER ERROR: empty parsertable");
        $this->lexer = $lexer;
        $this->table = $table;
        $this->productions = $productions;
        $this->stack = array(0);
        $this->verbose = $verbose;
        if ($this->verbose)
        for ($i = 0; $i < count($productions); $i++)
        {
            print $i.": ".$productions[$i][0]." ::= ".join(" ", $productions[$i][1])."\n";
        }
    }
    
    function top()
    {
        return $this->stack[count($this->stack)-1];
    }
    
    function run (& $callback)
    {
        $token = array();
        $token = $this->lexer->nextToken();
        $callback->shift($token, $this->stack);
        $action = '';
        $treestack = array();
        $tree    = array('S\'', array());
        $current = & $tree;
        while ($action != 'acc')
        {
            if (!($action = $this->table[$this->top()][$token['type']]))
            {
                print ("<b>Parsing error:</b> at token $token[value] of $token[type] at $token[line]:$token[charno] <br/>\n");
                return false;
            }
            if ($action[0] == 's')
            {
                $step = (int) substr($action, 1);
                if ($this->verbose)
                {
                    print "shift $token[value], ";
                    print "push $token[value] ";
                }
                $current[1][] = $token;
                $this->stack[] = $token['type'];
                if ($this->verbose)
                    print "push $step\n";
                $this->stack[] = $step;
                if ($this->lexer->hasNextToken())
                    $token = $this->lexer->nextToken();
                else
                {
                    print ("<b>Parsing error:</b> no next token<br/>");
                    return false;
                }
                $callback->shift($token, $this->stack);
            }
            elseif ($action[0] == 'r')
            {
                $rule = (int) substr($action, 1);
                if ($this->verbose)
                    print "$token[type] reduce $rule, ";
                for ($i = 0; $i < count($this->productions[$rule][1]); $i++)
                {
                    if ($this->verbose)
                        print "poppop ";
                    array_pop($this->stack);
                    $tokenname = array_pop($this->stack);
                    //print "tokenname: ". $tokenname."\n";
                }
                $callback->reduce($rule, $this->productions[$rule], $this->stack);
                if (!($goto = $this->table[$this->top()][$this->productions[$rule][0]]))
                {
                    print ("<b>Parsing error:</b> table error no goto for token ".$this->productions[(int)$rule][0]."<br/>\n");
                    return false;
                }
                if ($this->verbose)
                    print "goto for ".$this->productions[$rule][0]." is $goto\n";
                $this->stack[] = $this->productions[$rule][0];
                $this->stack[] = $goto;
            }
            if ($this->verbose)
                print "stack: ".join(" ", $this->stack)."\n";
        }
        $callback->reduce(0, $this->productions[0], $this->stack);
        return true;
    }

}


?>